﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.XPath;
using System.Net;
using System.IO;
using System.Timers;
using System.Threading;
using System.Windows.Forms;
using log4net;
using log4net.Config;

namespace FaceCompare
{
    class FaceProcessor
    {
        protected System.Timers.Timer m_recognizeTimer;
        protected string m_recognizeId;
        protected FaceCompareEntities m_db = new FaceCompareEntities();
        protected face m_face;
        protected static UTF8Encoding Encoding = new UTF8Encoding();
        private static readonly ILog logger =
          LogManager.GetLogger(typeof(FaceProcessor));


        public FaceProcessor(face facef)
        {
            DOMConfigurator.Configure();
            m_face = facef;
        }

        public void Start()
        {
           
            //if (m_face.status == 0)
            //{
                try
                {
                    logger.Info("new face " + m_face.id + " face fid: " + m_face.face_id);               
                    CompareToPreviousFaces();
                }
                catch (Exception ex)
                {
                  //  MessageBox.Show(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
                }
            //}
            //else
            //{
            //    Finish();
            //}
        }

        private void CompareToPreviousFaces()
        {

            var faces = m_db.face.Where(p => p.id < m_face.id && p.betaImageId != m_face.betaImageId).ToList();
            var faces1 = new List<face>();
            if (faces.Count() < 1)
            {
            //    var fa = m_db.face.First(p => p.face_id == m_face.face_id);
            //    fa.status = 1;
            //    m_db.SaveChanges();
            //    Finish();
              return;
            }


            while (faces.Count() > 0)
            {
                var rangeCount = Math.Min(faces.Count(), 10);
                var facesRange = faces.GetRange(0, rangeCount);
                faces.RemoveRange(0, rangeCount);
                SendRequestForCompare(facesRange);
            }
        }

        private void SendRequestForCompare(List<face> faces)
        {
            var request = (HttpWebRequest)WebRequest.Create("http://www.betafaceapi.com/service.svc/Faces_Recognize");
            request.Timeout = 1000000000;
            request.Method = "POST";
            request.ContentType = "application/xml";
            var requestBody = new StringBuilder();
            requestBody.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            requestBody.Append("<FacesRecognizeRequest xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
            requestBody.Append("<api_key>d45fd466-51e2-4701-8da8-04351c872236</api_key>");
            requestBody.Append("<api_secret>171e8465-f548-401d-b63b-caf0dc28df5f</api_secret>");
            requestBody.Append("<faces_uids>");
            requestBody.Append("<guid xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">" + m_face.face_id + "</guid>");
            requestBody.Append("</faces_uids>");
            requestBody.Append("<group_results>false</group_results>");
            requestBody.Append("<targets>");
            foreach (var f in faces)
            {
                requestBody.Append("<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">");
                requestBody.Append(f.face_id);
                logger.Info("request: " + f.face_id);
                requestBody.Append("</string>");
            }
            requestBody.Append("</targets>");

            requestBody.Append("</FacesRecognizeRequest>");
            request.ContentLength = requestBody.Length;
            using (Stream stream = request.GetRequestStream())
            {
                var requestBytes = Encoding.GetBytes(requestBody.ToString());
                stream.Write(requestBytes, 0, requestBytes.Length);
            }
            request.Timeout = 1000000000;
          
            var response = (HttpWebResponse)request.GetResponse();
            var doc = new XPathDocument(response.GetResponseStream());
            var nav = doc.CreateNavigator();
            var expr = nav.Compile("/BetafaceRecognizeRequestResponse/int_response");
            var iterator = nav.Select(expr);
            if (iterator.MoveNext() && iterator.Current.Value == "0")
            {
                expr = nav.Compile("/BetafaceRecognizeRequestResponse/recognize_uid");
                iterator = nav.Select(expr);
                iterator.MoveNext();
                m_recognizeId = iterator.Current.Value;
                m_recognizeTimer = new System.Timers.Timer(2000);
                var finishedEvent = new ManualResetEvent(false);
                m_recognizeTimer.Elapsed += (sender, e) => m_recognizeTimer_Elapsed(sender, e, finishedEvent);
                m_recognizeTimer.AutoReset = false;
                m_recognizeTimer.Start();
                finishedEvent.WaitOne(Timeout.Infinite, true);
            }
        }

        void m_recognizeTimer_Elapsed(object sender, ElapsedEventArgs e, ManualResetEvent finishedEvent)
        {
            m_recognizeTimer.Stop();
            var request = (HttpWebRequest)WebRequest.Create("http://www.betafaceapi.com/service.svc/GetRecognizeResult");
            request.Timeout = 1000000000;
            request.Method = "POST";
            request.ContentType = "application/xml";
            var requestBody = new StringBuilder();
            requestBody.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            requestBody.Append("<RecognizeResultRequest xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
            requestBody.Append("<api_key>d45fd466-51e2-4701-8da8-04351c872236</api_key>");
            requestBody.Append("<api_secret>171e8465-f548-401d-b63b-caf0dc28df5f</api_secret>");
            requestBody.Append("<recognize_uid>" + m_recognizeId + "</recognize_uid>");
            requestBody.Append("</RecognizeResultRequest>");

            request.ContentLength = requestBody.Length;
            using (Stream stream = request.GetRequestStream())
            {
                var requestBytes = Encoding.GetBytes(requestBody.ToString());
                stream.Write(requestBytes, 0, requestBytes.Length);
            }
            request.Timeout = 1000000000;
            try
            {
                var response = (HttpWebResponse)request.GetResponse();
                var doc = new XPathDocument(response.GetResponseStream());
                var nav = doc.CreateNavigator();
                var expr = nav.Compile("/BetafaceRecognizeResponse/int_response");
                var iterator = nav.Select(expr);
                if (iterator.MoveNext())
                {
                    if (iterator.Current.Value == "0")
                    {
                        ProcessFaces(nav);
                        finishedEvent.Set();
                        return;
                    }

                }
                m_recognizeTimer.Start();
            }
            catch(Exception ex)
            {
                var image = m_db.picture.First(p => p.fileName == m_face.fileName);
                var face = m_db.face.First(p => p.id == m_face.id);
                face.status = 30;
                image.status = 400;
                m_db.SaveChanges();
                Finish();
            }
        }

        private void ProcessFaces(XPathNavigator nav)
        {

            var expr = nav.Compile("/BetafaceRecognizeResponse/faces_matches/FaceRecognizeInfo/face_uid");
            var iterator = nav.Select(expr);
            if (iterator.Count == 0)
                return;
            string face_id = "";
            if (iterator.MoveNext())
            {
                face_id = iterator.Current.Value;
            }

            expr = nav.Compile("/BetafaceRecognizeResponse/faces_matches/FaceRecognizeInfo/matches/PersonMatchInfo");
            iterator = nav.Select(expr);
            while (iterator.MoveNext())
            {
                nav = iterator.Current;
                expr = nav.Compile("confidence");
                var iterator1 = nav.Select(expr);

                if (iterator1.MoveNext())
                {
                    var faceM = new faceMatch();
                    faceM.face1 = face_id;
                    faceM.matchPers = float.Parse(iterator1.Current.Value);
                    nav = iterator.Current;
                    expr = nav.Compile("face_uid");
                    
                    iterator1 = nav.Select(expr);

                    if (iterator1.MoveNext())
                    {
                        faceM.face2 = iterator1.Current.Value;
                        logger.Info("match: 1 " + iterator1.Current.Value + " -2 " + face_id);
                    }

                    m_db.AddTofaceMatch(faceM);
                    m_db.SaveChanges();

                }
            }
            logger.Info("--------------------------------------\n\n\n\n");
            var fa = m_db.face.First(p => p.face_id == m_face.face_id);
            fa.status = 1;
            m_db.SaveChanges();
            Finish();
        }

        private void Finish()
        {
         
                var pic = m_db.picture.First(p => p.betaImageId == m_face.betaImageId);
                if (pic.status != 400)
                {
                    pic.status = 20;
                    m_db.SaveChanges();
                }
               return;
          
        }
    }
}
